//
//  LcTimerOnce.m
//  18Birdies
//
//  Created by weihua on 2017/3/16.
//  Copyright © 2017年 Lc. All rights reserved.
//

#import "LcTimerOnce.h"

static const uint64_t kTimerLeeway   =  1 * NSEC_PER_SEC; // 1 second

@interface LcTimerOnce ()

@property (nonatomic, strong) dispatch_queue_t internalQueue;
@property (nonatomic, strong) dispatch_source_t gcdTimer;
@property (nonatomic, strong) NSDate * fireDate;
@property (nonatomic, copy) TimerOnceBlock block;

@end

@implementation LcTimerOnce

+ (instancetype)timerWithBlock:(TimerOnceBlock)block {
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  return [self timerWithBlock:block queue:queue];
}

+ (instancetype)timerWithBlock:(TimerOnceBlock)block queue:(dispatch_queue_t)queue {
  return [[self alloc] initWithBlock:block queue:queue];
}

+ (instancetype)timerWithBlock:(TimerOnceBlock)block startIn:(NSTimeInterval)startIn {
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  LcTimerOnce* timer = [[self alloc] initWithBlock:block queue:queue];
  [timer startIn:startIn];
  return timer;
}

- (instancetype)initWithBlock:(TimerOnceBlock)block queue:(dispatch_queue_t)queue {
  self = [super init];
  if (self) {
    _block = [block copy];
    _internalQueue = queue;
  }
  return self;
}

- (void)startIn:(NSTimeInterval)startIn {
  [self cancel];
  
  self.fireDate = [[NSDate alloc] initWithTimeIntervalSinceNow:startIn];
  self.gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.internalQueue);
  
  dispatch_source_set_timer(self.gcdTimer,
                            dispatch_walltime(NULL, startIn * NSEC_PER_SEC),
                            DISPATCH_TIME_FOREVER,
                            kTimerLeeway);
  
  dispatch_resume(self.gcdTimer);
  
  __weak typeof(self) weakSelf = self;
  dispatch_source_set_event_handler(self.gcdTimer, ^{
    dispatch_sync(dispatch_get_main_queue(), ^{
      __strong typeof (weakSelf) strongSelf = weakSelf;
      if (strongSelf.block != nil) {
        strongSelf.block(strongSelf);
      }
    });
  });
}

- (NSTimeInterval)fireIn {
  if (self.fireDate == nil) {
    return 0;
  }
  return [self.fireDate timeIntervalSinceNow];
}

- (void)cancel {
  if (self.gcdTimer != nil) {
    dispatch_cancel(self.gcdTimer);
  }
  self.gcdTimer = nil;
}

- (void)dealloc {
  [self cancel];
}

@end
